﻿namespace LogManager.Utils.Xml
{
    using NUnit.Framework;
    using System.Collections.Generic;
    using System.Linq;
    using System.Xml.Linq;

    public class TestXmlResolver
    {
        [Test]
        public void XmlResolver_Children_RetreiveChildNodes_NodesRetrevied()
        {
            var str = @"<Library>
                        <Tablet>android</Tablet>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        <Book>jack bower</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var books = XmlResolver.Children(xElm, (Library l) => l.Books);

            Assert.AreEqual(books.Count(), 3);
            Assert.AreEqual(books.ElementAt(0).ToString(), "<Book>martin</Book>");
            Assert.AreEqual(books.ElementAt(1).ToString(), "<Book>tom and jerry</Book>");
            Assert.AreEqual(books.ElementAt(2).ToString(), "<Book>jack bower</Book>");
        }

        [Test]
        public void XmlResolver_Children_NoChildsToRetreive_NothingRetrevied()
        {
            var str = @"<Library>
                        <Tablet>android</Tablet>
                        <Other>tom and jerry</Other>
                        </Library>";
            var xElm = XElement.Parse(str);
            var books = XmlResolver.Children(xElm, (Library l) => l.Books);

            Assert.AreEqual(books.Count(), 0);
        }

        [Test]
        public void XmlResolver_Children_EmptyParent_NothingRetrevied()
        {
            var str = @"<Library>
                        </Library>";
            var xElm = XElement.Parse(str);
            var books = XmlResolver.Children(xElm, (Library l) => l.Books);

            Assert.AreEqual(books.Count(), 0);
        }

        [Test]
        public void XmlResolver_GetAttributeValue_Attribute_GoodValue()
        {
            var str = @"<Library Name=""kaka"" />";
            var xElm = XElement.Parse(str);
            var value = XmlResolver.GetAttributeValue(xElm, (Library l) => l.Name);

            Assert.AreEqual(value, "kaka");
        }

        [Test]
        public void XmlResolver_GetAttributeValueFiledNode_Attribute_GoodValue()
        {
            var str = @"<Library Name=""kaka"">
                            <Tablet type=""intel"">android</Tablet>
                        </Library>";
            var xElm = XElement.Parse(str);
            var value = XmlResolver.GetAttributeValue(xElm, (Library l) => l.Name);

            Assert.AreEqual(value, "kaka");
        }

        [Test]
        public void XmlResolver_GetAttributeValue_AttributeBool_GoodValue()
        {
            var str = @"<Library IsOpen=""true""/>";
            var xElm = XElement.Parse(str);
            var value = XmlResolver.GetAttributeValue(xElm, (Library l) => l.IsOpen);

            Assert.AreEqual(value, true);
        }

        [Test]
        public void XmlResolver_SetAttributeValue_AttributeBool_GoodValue()
        {
            var str = @"<Library IsOpen=""true"" />";
            var xElm = XElement.Parse(str);
            XmlResolver.SetAttributeValue(xElm, (Library l) => l.IsOpen, false);

            Assert.AreEqual(xElm.ToString(), @"<Library IsOpen=""false"" />");
        }

        [Test]
        public void XmlResolver_SetAttributeValue_AttributeString_GoodValue()
        {
            var str = @"<Library Name=""previous"" />";
            var xElm = XElement.Parse(str);
            XmlResolver.SetAttributeValue(xElm, (Library l) => l.Name, "next");

            Assert.AreEqual(xElm.ToString(), @"<Library Name=""next"" />");
        }

        [Test]
        public void XmlResolver_SetAttributeValue_NoAttribute_AttributeCreated()
        {
            var str = @"<Library />";
            var xElm = XElement.Parse(str);
            XmlResolver.SetAttributeValue(xElm, (Library l) => l.Name, "next");

            Assert.AreEqual(xElm.ToString(), @"<Library Name=""next"" />");
        }

        [Test]
        public void XmlResolverT_Ctor_BaseElementProvided_InstanceProvidedIsKept()
        {
            var str = @"<Library />";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);

            Assert.AreEqual(xml.BaseNode, xElm);
        }

        [Test]
        public void XmlResolverT_Children_MixedElements_RightOnesRetreived()
        {
            var str = @"<Library>
                        <Tablet>android</Tablet>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        <Book>jack bower</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var books = xml.Children(p => p.Books);

            Assert.AreEqual(books.Count(), 3);
            Assert.AreEqual(books.ElementAt(0).ToString(), "<Book>martin</Book>");
            Assert.AreEqual(books.ElementAt(1).ToString(), "<Book>tom and jerry</Book>");
            Assert.AreEqual(books.ElementAt(2).ToString(), "<Book>jack bower</Book>");
        }

        [Test]
        public void XmlResolverT_Children_HighLevelPluralProperty_RightOnesRetreived()
        {
            var str = @"<Library>
                        <Tablet>android</Tablet>
                        <SuperBook>martin</SuperBook>
                        <SuperBook>tom and jerry</SuperBook>
                        <SuperBook>jack bower</SuperBook>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var books = xml.Children(p => p.SuperBooks);

            Assert.AreEqual(books.Count(), 3);
            Assert.AreEqual(books.ElementAt(0).ToString(), "<SuperBook>martin</SuperBook>");
            Assert.AreEqual(books.ElementAt(1).ToString(), "<SuperBook>tom and jerry</SuperBook>");
            Assert.AreEqual(books.ElementAt(2).ToString(), "<SuperBook>jack bower</SuperBook>");
        }

        [Test]
        public void XmlResolverT_Child_MixedElementsPluralProperty_RightOneRetreived()
        {
            var str = @"<Library>
                        <Tablet>android</Tablet>
                        <Book>martin</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var book = xml.Child(p => p.Books);

            Assert.AreEqual(book.ToString(), "<Book>martin</Book>");
        }

        [Test]
        public void XmlResolverT_Child_MixedElementsSingleProperty_RightOneRetreived()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var owner = xml.Child(p => p.LibraryOwner);

            Assert.AreEqual(owner.ToString(), "<LibraryOwner>Mr Jack</LibraryOwner>");
        }

        [Test]
        public void XmlResolverT_Child_PropertyWithS_InstanceResolved()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Smarties>piou piou</Smarties>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var child = xml.Child(l => l.Smarties);

            Assert.AreEqual(child.ToString(), @"<Smarties>piou piou</Smarties>");
        }

        [Test]
        public void XmlResolverT_Child_NotExisting_NullReturned()
        {
            var str = @"<Library>
                        <Book>martin</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var owner = xml.Child(p => p.LibraryOwner);

            Assert.AreEqual(owner, null);
        }

        [Test]
        public void XmlResolverT_GetAttributeValue_Attribute_GoodValue()
        {
            var str = @"<Library Name=""kaka"" />";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var value = xml.GetAttributeValue(l => l.Name);

            Assert.AreEqual(value, "kaka");
        }

        [Test]
        public void XmlResolverT_GetAttributeValueFiledNode_Attribute_GoodValue()
        {
            var str = @"<Library Name=""kaka"">
                            <Tablet type=""intel"">android</Tablet>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var value = xml.GetAttributeValue(l => l.Name);

            Assert.AreEqual(value, "kaka");
        }

        [Test]
        public void XmlResolverT_GetAttributeValue_AttributeBool_GoodValue()
        {
            var str = @"<Library IsOpen=""true""/>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var value = xml.GetAttributeValue(l => l.IsOpen);

            Assert.AreEqual(value, true);
        }

        [Test]
        public void XmlResolverT_SetAttributeValue_AttributeBool_GoodValue()
        {
            var str = @"<Library IsOpen=""true"" />";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            xml.SetAttributeValue(l => l.IsOpen, false);

            Assert.AreEqual(xElm.ToString(), @"<Library IsOpen=""false"" />");
        }

        [Test]
        public void XmlResolverT_SetAttributeValue_AttributeString_GoodValue()
        {
            var str = @"<Library Name=""previous"" />";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            xml.SetAttributeValue(l => l.Name, "next");

            Assert.AreEqual(xElm.ToString(), @"<Library Name=""next"" />");
        }

        [Test]
        public void XmlResolverT_SetAttributeValue_NoAttribute_AttributeCreated()
        {
            var str = @"<Library />";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            xml.SetAttributeValue(l => l.Name, "next");

            Assert.AreEqual(xElm.ToString(), @"<Library Name=""next"" />");
        }

        [Test]
        public void XmlResolverT_ResolveChild_SimpleCase_InstanceResolved()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var owner = xml.ResolveChild(l => l.LibraryOwner, elm => new Owner() { FirstName = elm.Value });

            Assert.AreEqual(owner.FirstName, @"Mr Jack");
        }

        [Test]
        public void XmlResolverT_ResolveChildren_SimpleCase_InstancesResolved()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);
            var books = xml.ResolveChildren(l => l.Books, elm => new Book() { Title = elm.Value });

            Assert.AreEqual(books.Count(), 2);
            Assert.AreEqual(books.ElementAt(0).Title, @"martin");
            Assert.AreEqual(books.ElementAt(1).Title, @"tom and jerry");
        }

        [Test]
        public void XmlResolverT_SetChild_SimpleCase_XelementUpdated()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);

            var updatedOwner = XElement.Parse("<LibraryOwner>Mr Jack2</LibraryOwner>");
            xml.SetChild(l => l.LibraryOwner, updatedOwner);

            var expected = @"<Library>
                        <LibraryOwner>Mr Jack2</LibraryOwner>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";

            Assert.AreEqual(xElm.ToString().Replace(" ", ""), expected.Replace(" ", ""));
        }

        [Test]
        public void XmlResolverT_SetChild_NullNode_NodeRemoved()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);

            xml.SetChild(l => l.LibraryOwner, null);

            var expected = @"<Library>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";

            Assert.AreEqual(xElm.ToString().Replace(" ", ""), expected.Replace(" ", ""));
        }

        [Test]
        public void XmlResolverT_SetChildren_SimpleCase_XelementsUpdated()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);

            var updatedBooks = XElement.Parse(@"<a><Book>martin2</Book>
                        <Book>tom and jerry2</Book></a>").Descendants();

            xml.SetChildren(l => l.Books, updatedBooks);

            var expected = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin2</Book>
                        <Book>tom and jerry2</Book>
                        </Library>";

            Assert.AreEqual(xElm.ToString().Replace(" ", ""), expected.Replace(" ", ""));
        }

        [Test]
        public void XmlResolverT_SetChildren_NullElement_NodesRemoved()
        {
            var str = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        <Book>martin</Book>
                        <Book>tom and jerry</Book>
                        </Library>";
            var xElm = XElement.Parse(str);
            var xml = new XmlResolver<Library>(xElm);

            xml.SetChildren(l => l.Books, null);

            var expected = @"<Library>
                        <LibraryOwner>Mr Jack</LibraryOwner>
                        </Library>";

            Assert.AreEqual(xElm.ToString().Replace(" ", ""), expected.Replace(" ", ""));
        }
    }

    public class Library
    {
        public bool IsOpen { get; set; }
        public string Name { get; set; }
        public IEnumerable<Book> Books { get; set; }
        public SuperBookCollection SuperBooks { get; set; }

        public Owner LibraryOwner { get; set; }
        public string Smarties { get; set; }
    }

    public class Book
    {
        public string Title { get; set; }
    }

    public class Owner
    {
        public string FirstName { get; set; }
    }

    public class SuperBookCollection : List<Book>
    {
    }
}
